package com.glzn.springboot03.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.glzn.springboot03.entity.Para;
import com.glzn.springboot03.mapper.ParaMapper;
import com.glzn.springboot03.mqtt.MqttPublishClient;
import com.glzn.springboot03.service.ParaService;
import com.glzn.springboot03.util.ComService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Lenovo
 */
@Service
public class ParaServiceImpl implements ParaService {
	private final static Logger logger = LoggerFactory.getLogger(EventServiceImpl.class);

	private final static int MAX_OPTION_COUNT = 16;

	private final static int OPERATE_SUCCESS = 0x16;

	private final static int PARA_TYPE_DATA = 1;

	private final static int PARA_TYPE_OPTION = 2;

	private Map<String, Integer> map = new HashMap<>();

	@Autowired
	private ParaMapper paraMapper;

	@Override
	public List<Para> findAll(Integer deviceId) {
		return paraMapper.findAll(deviceId);
	}

	@Override
	public PageInfo<Para> findByAreaNum(int areaNum, int pageNum, int pageSize, Integer deviceId) {
		Page<Para> page = PageHelper.startPage(pageNum, pageSize);
		paraMapper.findByAreaNum(areaNum, deviceId);
		return new PageInfo<>(page);
	}

	@Override
	public PageInfo<Para> findByTypeAndAreaNum(String type, int areaNum, int pageNum, int pageSize, Integer deviceId) {
		Page<Para> page = PageHelper.startPage(pageNum, pageSize);
		paraMapper.findByTypeAndAreaNum(type, areaNum, deviceId);
		return new PageInfo<>(page);
	}

	@Override
	public List<Para> findByAreaNum(int areaNum, Integer deviceId) {
		return paraMapper.findByAreaNum(areaNum, deviceId);
	}

	@Override
	public List<Para> findByTypeAndAreaNum(String type, int areaNum, Integer deviceId) {
		return paraMapper.findByTypeAndAreaNum(type, areaNum, deviceId);
	}

	@Override
	public boolean insert(Para para) {
		return paraMapper.insert(para);
	}

	@Override
	public boolean deleteAll(Integer deviceId) {
		return paraMapper.deleteAll(deviceId);
	}

	@Override
	public int getProcess(String sessionId) {
		Integer process = map.get(sessionId);
		if (process == null){
			process = 0;
		}
		if (process == 100) {
			map.remove(sessionId);
		}
		return process;
	}

	@Override
	public String listPara() throws UnsupportedEncodingException {
		HttpSession session = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getSession();
		MqttPublishClient mqttPublishClient = (MqttPublishClient) session.getAttribute("mqttPublishClient");
		Integer deviceId = (Integer) session.getAttribute("device_id");
		String sessionId = session.getId();

		if (mqttPublishClient == null) {
			return "device";
		}
		int process = 0;
		//1、made数据
		ComService comService = new ComService();
		comService.setServerType(0x3);
		comService.setCmd(0x20);
		//发送并接收数据
		byte[] data = mqttPublishClient.sendAndRecv(comService, 1000*20, false);
		if(null == data){
			logger.info("recv data failed");
			return "failed";
		}
		//解析数据
		int curChannelNum = data[7];
		int channelCount = data[8];
		logger.info("channel count{}", channelCount);
		List<Para> paraList = new ArrayList<>();
		for (int i = 0; i < channelCount; i++){
			comService.setServerType(0x3);
			comService.setCmd(0x10);
			//接收数据
			byte[] data1 = mqttPublishClient.sendAndRecv(comService, 1000*20, false);
			int errTimes=0;
			while (null == data1){
				errTimes++;
				if(errTimes > 3) {
					return "failed";
				}
				data1 = mqttPublishClient.sendAndRecv(comService, 1000*20, true);
			}
			//定值数量
			int paraCount = ((data1[8] & 0xFF) << 8) | (data1[7] & 0xFF);
			int startIndex = 0;
			int len = 0;
			do {
				comService.setServerType(0x3);
				comService.setCmd(0x11);
				comService.setAreaNum((byte) i);
				comService.setParaNum(startIndex);
				//接收数据
				byte[] data2 = mqttPublishClient.sendAndRecv(comService, 1000*20, false);
				errTimes = 0;
				while(null == data2){
					errTimes++;
					if(errTimes > 3) {
						return "failed";
					}
					data2 = mqttPublishClient.sendAndRecv(comService, 1000*20, true);
				}
				Para para = parsePara(data2, i, startIndex, deviceId);
				if (para == null){
					continue;
				}
				paraList.add(para);
				startIndex++;
				//进度
				process = 100*startIndex/paraCount/channelCount + i*100/channelCount;
				map.put(sessionId, process);
			} while (startIndex < paraCount);
		}
		//将数据保存到数据库
		deleteAll(deviceId);
		for (Para para : paraList) {
			insert(para);
		}
		process = 100;
		map.put(sessionId, process);
		return "success";
	}

	public Para parsePara(byte[] data2, int i, int startIndex, Integer deviceId) throws UnsupportedEncodingException {
		int pos = 7;
		int len = 0;
		//解析数据
		if (data2[pos++] != OPERATE_SUCCESS) {
			logger.info("recv data error");
			return null;
		}
		//通道编号
		int paraNum = ((data2[pos + 1] & 0xFF) << 8) | (data2[pos] & 0xFF);
		pos += 2;
		int format = data2[pos++];
		logger.info("paraNum:{}", paraNum);
		//类型
		len = 20;
		String type = new String(data2, pos, len, "gbk");
		type = type.replaceAll("\u0000","");
		logger.debug("type:{}", type);
		pos += len;
		//名称
		len = 32;
		String name = new String(data2, pos, len, "gbk");
		name = name.replaceAll("\u0000","");
		logger.debug("name:{}", name);
		pos += len;
		//value
		int value = ((data2[pos + 1] & 0xFF) << 8) | (data2[pos] & 0xFF);
		pos += 2;
		//最大值
		int maxValue = ((data2[pos + 1] & 0xFF) << 8) | (data2[pos] & 0xFF);
		pos += 2;
		//最小值
		int minValue = ((data2[pos + 1] & 0xFF) << 8) | (data2[pos] & 0xFF);
		pos += 2;
		//数值型
		if (format == PARA_TYPE_DATA) {
			int intBitNum = data2[pos++] & 0xFF;
			int decBitNum = data2[pos++] & 0xFF;
			len = 8;
			String unit = new String(data2, pos, len, "gbk");
			unit = unit.replaceAll("\u0000","");
			logger.debug("unit:{}", unit);
			pos += len;
			return new Para(startIndex, paraNum, i, format, type, name, value, maxValue, minValue, intBitNum, decBitNum, unit, deviceId);
		} else if (format == PARA_TYPE_OPTION) {
			StringBuilder option = new StringBuilder();
			for (int j = 0; j < MAX_OPTION_COUNT; j++) {
				len = 32;
				String str = new String(data2, pos, len, "gbk");
				str = str.replaceAll("\u0000","");
				pos += len;
				if (option.length() > 0) {
					option.append("、");
				}
				if (!"".equals(str)) {
					option.append(str);
				}
			}
			return new Para(startIndex, paraNum, i, format, type, name, value, maxValue, minValue, option.toString(), deviceId);
		}else {
			return null;
		}
	}
}
